/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package tman.system.peer.tman;

import common.peer.PeerAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
import se.sics.asdistances.ASDistances;

/**
 *
 * @author gianmariospacagna
 */
public class SWNBuilder {

    private static final int n = 8191;
    private static final double r = 1;
    //private static final float k = 100;

    public SWNBuilder() {
    }

    public static ArrayList<PeerAddress> rewire(PeerAddress node, Map<Integer, PeerAddress> view, int k) {

        Map<Integer, PeerAddress> tmpView = new HashMap<Integer, PeerAddress>();

        //keep the succ and pred to not break the ring
        /*
        try {
        RingNode[] succPred = RingBuilder.succPred(node, view.values());
        for (RingNode ringNode : succPred) {
        tmpView.put(ringNode.getDistance(), ringNode.getSelf());
        }
        
        } catch (ExceptionViewEmpty ex) {
        //Logger.getLogger(SWNBuilder.class.getName()).log(Level.SEVERE, null, ex);
        }*/

        double norm = 0;

        for (Integer dist : view.keySet()) {
            norm += Math.pow((1 / (double) dist), r);
        }

        //rewire other links with prob=1/d
        for (Integer dist : view.keySet()) {
            double prob = (Math.pow((1 / (double) dist), r) / norm);
            prob = Math.pow(prob, k);
            Random random = new Random();
            float u = random.nextFloat();
            //System.err.println("Peer " + node + " and " + view.get(dist) + " dist = " + dist + " prob = " + prob + " u = "+u);
            if (prob > u) {
                tmpView.put(dist, view.get(dist));
            }
        }


        return new ArrayList<PeerAddress>(tmpView.values());
    }

    public static ArrayList<PeerAddress> rank(PeerAddress node, Set<PeerAddress> buf) {

        Map<Integer, PeerAddress> tmpView = new TreeMap<Integer, PeerAddress>();
        /*keep the succ and pred to not break the ring*/
       // System.err.println(node+"-->"+"buf to rank is: "+buf);
        try {
            int i = 0;
            RingNode[] succPred = RingBuilder.succPred(node, buf);
            for (RingNode ringNode : succPred) {
                tmpView.put(i++, ringNode.getSelf());
                buf.remove(ringNode.getSelf());
            }

        } catch (ExceptionViewEmpty ex) {
            //Logger.getLogger(SWNBuilder.class.getName()).log(Level.SEVERE, null, ex);
        }



        double norm = 0;

        for (PeerAddress peer : buf) {
            if (peer != node) {
                norm += prob2(node, peer);
            }
        }

        //System.err.println("Peer " + node + " , norm = " + norm);
        Random random = new Random();
        for (PeerAddress peer : buf) {
            if (peer != node) {

                double prob = prob2(node, peer);
                prob /= norm;

                
                float u = random.nextFloat();
                int rank = (int) (100 / (prob * u));
                //System.err.println("Peer " + node + " and " + peer + " dist = " + dist + " prob = " + prob);
                tmpView.put(rank, peer);
            }
        }

        //System.err.println("Peer " + node + " , TreeMap = " + tmpView);
        //ArrayList<PeerAddress> rankedArray = new ArrayList<PeerAddress>(tmpView.values());
        //i=index of the tmpview
        
        //System.err.println("SWNBuilder-> Peer " + node + " : TmpViewValues = " + tmpView.values());
        
        int i=0;
        
        int size=tmpView.size();
        int j1=2;
        int j2=(int)((size-2)/2)+2;
        
        PeerAddress[] rankedArray = new PeerAddress[size];
        for (PeerAddress peer: tmpView.values()){
            if(i<2){
                rankedArray[i]=peer;
                
            }else if (i%2==1){
                rankedArray[j1]=peer;
                j1++;
            }else{
                rankedArray[j2]=peer;
                j2++;
            }
            i++;
                
        }
        ArrayList<PeerAddress> rankedList = new ArrayList<PeerAddress>(Arrays.asList(rankedArray));
        
        //System.err.println("SWNBuilder-> Peer " + node + " : RankedList = " + rankedList);
        return rankedList;
    }

    private static double prob(PeerAddress ref, PeerAddress peer) {

        int dist = RingBuilder.distance(ref, peer);
        double prob = (Math.pow((1 / (double) dist), r));
        return prob;
    }

    private static double prob2(PeerAddress ref, PeerAddress peer) {

        int dist = RingBuilder.distance(ref, peer);
        int distAS= ASDistances.getInstance().getDistance(ref.getPeerAddress().getIp().getHostAddress(), peer.getPeerAddress().getIp().getHostAddress());
        distAS++;
        //System.err.println("AS Distance between: "+ref+" and "+peer+ "is "+distAS);
        double prob = (Math.pow((1 / (double) (dist*distAS)), r));
        return prob;
    }
    
}
